﻿namespace Snipps.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.Windows.Input;
    using System.Windows.Threading;

    using GalaSoft.MvvmLight.Command;
    using GalaSoft.MvvmLight.Messaging;

    using Snipps.Core;
    using Snipps.Core.Entities;
    using Snipps.Infrastructure;
    using Snipps.Infrastructure.Services;
    using Snipps.Messages;
    using Snipps.Services;
    using Snipps.Validators;
    using Snipps.ViewModels.Pages;

    public class MainViewModel : BindableBase
    {
        public const string CurrentPagePropertyName = "CurrentPage";

        public const string CurrentStatusPropertyName = "CurrentStatus";

        public const string IsBusyPropertyName = "IsBusy";

        private readonly DispatcherTimer _autoSaveTimmer;

        private readonly EditSnippetViewModel _editSnippetVM;

        private readonly EditTagViewModel _editTagVM;

        private readonly IExportImportSnippetService _exportImportSnippetService;

        private readonly FilterViewModel _filterVM;

        private readonly List<IPageViewModel> _pages;

        private readonly ISettings _settings;

        private readonly ISnippetsDataContext _snippetsDataContext;

        private readonly IWindowOperationsService _windowOperationsService;

        private IPageViewModel _currentPage;

        private string _currentStatus;

        private bool _isBusy;

        private IPageViewModel _prevPage;

        public MainViewModel(
            IWindowOperationsService windowOperationsService, 
            IExportImportSnippetService exportImportSnippetService, 
            ISnippetsDataContext snippetsDataContext, 
            IThreadingService threadingService, 
            ISettings settings)
        {
            _pages = new List<IPageViewModel>();
            _windowOperationsService = windowOperationsService;
            _exportImportSnippetService = exportImportSnippetService;

            _snippetsDataContext = snippetsDataContext;
            _settings = settings;

            _filterVM = new FilterViewModel(_snippetsDataContext, threadingService);

            _editSnippetVM = new EditSnippetViewModel(_snippetsDataContext, threadingService, exportImportSnippetService);

            _editTagVM = new EditTagViewModel(_snippetsDataContext) { Validator = new EditTagValidator() };

            _pages.Add(_filterVM);
            _pages.Add(_editSnippetVM);
            _pages.Add(_editTagVM);

            Messenger.Default.Register<EditItemMsg>(this, OnEditItem);

            Messenger.Default.Register<ItemChangedMsg>(this, OnItemSaved);
            Messenger.Default.Register<CancelMsg>(this, OnCancel);
            Messenger.Default.Register<ActivateItemMsg>(this, ActivateItem);
            Messenger.Default.Register<RemoveItemMsg>(this, OnRemoveItem);
            ShowCommand = new RelayCommand(() => _windowOperationsService.ShowWindow());
            HideCommand = new RelayCommand(() => _windowOperationsService.HideWindow());
            ToggleVisibilityCommand = new RelayCommand(OnToggleVisibility);
            WindowCommands.HideCommand = HideCommand;

            CloseCommand = new RelayCommand(() => _windowOperationsService.CloseWindow());

            _autoSaveTimmer = new DispatcherTimer();
            var minutes = settings.AutoSaveTimeInterval;
            _autoSaveTimmer.Interval = new TimeSpan(0, minutes, 0);
            _autoSaveTimmer.Tick += AutoSaveTimmer_Tick;

            ChangePage(_filterVM);
            _autoSaveTimmer.Start();
        }

        public string CurrentStatus
        {
            get
            {
                return _currentStatus;
            }

            set
            {
                SetProperty(ref _currentStatus, value, CurrentStatusPropertyName);
            }
        }

        public bool IsBusy
        {
            get
            {
                return _isBusy;
            }

            set
            {
                SetProperty(ref _isBusy, value, IsBusyPropertyName);
            }
        }

        public ICommand CloseCommand { get; private set; }

        public ICommand HideCommand { get; private set; }

        public ICommand ShowCommand { get; private set; }

        public ICommand ToggleVisibilityCommand { get; private set; }

        public IPageViewModel CurrentPage
        {
            get
            {
                return _currentPage;
            }

            set
            {
                SetProperty(ref _currentPage, value, CurrentPagePropertyName);
            }
        }

        public string CurrentMode { get; set; }

        public ProgrammingLang ProgrammingLang { get; set; }

        private void OnToggleVisibility()
        {
            if (_windowOperationsService.IsVisible)
            {
                _windowOperationsService.HideWindow();
            }
            else
            {
                _windowOperationsService.ShowWindow();
            }
        }

        private void AutoSaveTimmer_Tick(object sender, EventArgs e)
        {
            _autoSaveTimmer.Stop();
            _snippetsDataContext.SaveChanges();
            _autoSaveTimmer.Start();
        }

        public override void Cleanup()
        {
            _snippetsDataContext.SaveChanges();
            _snippetsDataContext.Dispose();

            _settings.Save();
            base.Cleanup();
        }

        private void OnRemoveItem(RemoveItemMsg removeItemMsg)
        {
            if (removeItemMsg.Item is Tag)
            {
                _snippetsDataContext.RemoveTag(removeItemMsg.Item as Tag);
                _filterVM.SnippetFilter.StartFilterWhenActive = true;
            }
            else if (removeItemMsg.Item is Snippet)
            {
                _snippetsDataContext.RemoveSnippet(removeItemMsg.Item as Snippet);
            }
        }

        private bool CurrentViewContainsFilters()
        {
            if (CurrentPage is IHasSnippetsFilter || CurrentPage is IHasProgrammingLangsFilter || CurrentPage is IHasTagsFilter)
            {
                return true;
            }

            return false;
        }

        private void ActivateItem(ActivateItemMsg activateItemMsg)
        {
            if (CurrentViewContainsFilters())
            {
                if (activateItemMsg.Item is ProgrammingLang)
                {
                    ActivateProgrammingLang(activateItemMsg);
                }
                else if (activateItemMsg.Item is Tag)
                {
                    ActivateTag(activateItemMsg);
                }
                else if (activateItemMsg.Item is Snippet)
                {
                    ActivateSnippet(activateItemMsg);
                }
            }
        }

        private void ActivateSnippet(ActivateItemMsg activateItemMsg)
        {
            var viewWithSnippetFilter = CurrentPage as IHasSnippetsFilter;
            if (viewWithSnippetFilter != null && activateItemMsg.Item is Snippet)
            {
                var selectedSnippet = activateItemMsg.Item as Snippet;
                var filter = viewWithSnippetFilter.SnippetFilter;
                filter.StopFiltering();
                if (selectedSnippet.IsNew)
                {
                    EditItem(selectedSnippet);
                }
                else if (selectedSnippet.Content != null)
                {
                    _exportImportSnippetService.Export(selectedSnippet);
                }
            }
        }

        private void EditItem(object item)
        {
            if (item is Tag)
            {
                _editTagVM.EditedTag = item as Tag;
                ChangePage(_editTagVM);
            }
            else if (item is Snippet)
            {
                _editSnippetVM.EditedSnippet = item as Snippet;
                ChangePage(_editSnippetVM);
            }
        }

        private void ActivateTag(ActivateItemMsg activateItemMsg)
        {
            var viewWithTagFilter = CurrentPage as IHasTagsFilter;
            if (viewWithTagFilter != null && activateItemMsg.Item is Tag)
            {
                var filter = viewWithTagFilter.TagFilter;
                filter.StopFiltering();

                var selectedTag = activateItemMsg.Item as Tag;

                if (selectedTag.IsNew)
                {
                    _snippetsDataContext.SaveTag(selectedTag);
                    filter.FilterTags.Add(selectedTag);
                }
                else
                {
                    filter.FilterTags.Add(selectedTag);
                }

                filter.FilterText = string.Empty;
                filter.StartFiltering();
            }
        }

        private void ActivateProgrammingLang(ActivateItemMsg activateItemMsg)
        {
            var viewWithProgrammingLangFilter = CurrentPage as IHasProgrammingLangsFilter;
            if (viewWithProgrammingLangFilter != null && activateItemMsg.Item is ProgrammingLang)
            {
                var filter = viewWithProgrammingLangFilter.ProgrammingLangFilter;
                filter.StopFiltering();
                var selectedProgrammingLang = activateItemMsg.Item as ProgrammingLang;
                filter.FilterText = selectedProgrammingLang.Name;
                filter.CurrentProgrammingLang = selectedProgrammingLang;
            }
        }

        private void OnCancel(CancelMsg obj)
        {
            ChangePage(_prevPage);
        }

        private void OnItemSaved(ItemChangedMsg args)
        {
            if (args.Item is Snippet && _filterVM == _prevPage && _filterVM.CurrentFilter != null)
            {
                _filterVM.CurrentFilter.StartFilterWhenActive = true;
            }
            else if (args.Item is Tag)
            {
                _filterVM.SnippetFilter.StartFilterWhenActive = true;
            }

            ChangePage(_prevPage);
        }

        private void ChangePage(IPageViewModel pageToChange)
        {
            if (pageToChange == null)
            {
                throw new ArgumentNullException("pageToChange");
            }

            _prevPage = CurrentPage;

            foreach (var page in _pages)
            {
                page.IsActive = false;
            }

            pageToChange.IsActive = true;
            CurrentPage = pageToChange;
            CurrentStatus = pageToChange.Name;
        }

        private void OnEditItem(EditItemMsg editItemMsg)
        {
            EditItem(editItemMsg.Item);
        }
    }
}